home *** CD-ROM | disk | FTP | other *** search
- {**************************************************************************
- * Releases memory above the last MARK call made. *
- * Copyright (c) 1986 Kim Kokkonen, TurboPower Software. *
- * Released to the public domain for personal, non-commercial use only. *
- ***************************************************************************
- * Version 1.0 2/8/86 *
- * original public release *
- * (thanks to Neil Rubenking for an outline of the method used) *
- * Version 1.1 2/11/86 *
- * fixed problem with processes which deallocate their environment *
- * Version 1.2 2/13/86 *
- * fixed another problem with processes which deallocate environment *
- * Version 1.3 2/15/86 *
- * add support for "named" marks *
- * Version 1.4 2/23/86 *
- * add support for releasing programs which use Expanded Memory *
- * Version 1.5 2/28/86 *
- * add more bulletproof method of finding first allocation block *
- * Version 1.6 3/20/86 *
- * restore all FF interrupts. *
- * restore the termination address to the local process *
- * reduce number of EMS blocks to 32. *
- * fix bug in number of EMS handles in EMS release step *
- * restore an undocumented address in the PSP which allows RELEASE of *
- * a COMMAND shell (emulates the EXIT command) *
- * Version 1.7 (date not recorded) *
- * add "protected" marks *
- * Version 1.8 4/21/86 *
- * fix problem when mark is installed as 'MARK ' *
- * Version 1.9 5/22/86 *
- * release the environment of MARK when it is not contiguous with *
- * the MARK itself *
- * capture RELEASE calls from within batch files and don't release the *
- * batch control block *
- * fiddle with different methods of restoring interrupt vectors in *
- * an attempt to successfully remove DoubleDos. No success, not *
- * implemented. Note, after more effort: DDos apparently *
- * reprograms the 8259 as well as patching the operating system *
- * Version 2.0 6/17/86 *
- * support "file" marks placed by the new program FMARK *
- * Version 2.1 7/18/86 *
- * fix bug in restoring "parent" address in RELEASE PSP *
- * Version 2.2 3/3/87 *
- * add option to revector 8259 interrupt controller *
- * (thanks to Steve Glynn for this code) *
- * add option to leave mark in place when RELEASE is run *
- * restore save areas for EGA and interapplication communications *
- * Version 2.3 5/2/87 *
- * update watch area, if any, when releasing *
- * Version 2.4 5/17/87 *
- * avoids use of EMS call $4B, which doesn't work in many EMS *
- * implementations *
- * adds switch to ignore EMS altogether *
- * Version 2.5 6/2/87 *
- * check version number of mark to avoid incompatibilities *
- * *
- ***************************************************************************
- * telephone: 408-438-8608, CompuServe: 72457,2131. *
- * requires Turbo version 3 to compile. *
- * Compile with mAx dynamic memory = FFFF. *
- ***************************************************************************}
-
- {$P128}
- {$C-}
-
- program ReleaseTSR;
- {-Release system memory above the last mark call}
- {-Release expanded memory blocks allocated since the last mark call}
-
- const
- Version = '2.5';
- ProtectChar = '!'; {Marks whose name begins with this will be
- released ONLY if an exact name match occurs}
- MaxBlocks = 128; {Max number of DOS allocation blocks supported}
- MaxHandles = 32; {Max number of EMS allocation blocks supported}
- EMSinterrupt = $67; {The vector used by the expanded memory manager}
-
- MarkID = 'M2.5 PARAMETER BLOCK FOLLOWS'; {Marking string for TSR MARK}
- FmarkID = 'FM2.5 TSR'; {Marking string for TSR file mark}
-
- {Offsets into resident copy of MARK.COM for data storage}
- MarkOffset = $103; {Where markID is found in MARK TSR}
- FmarkOffset = $60; {Where fmarkID is found in FMARK TSR}
- VectorOffset = $120; {Where vector table is stored}
- EGAsavOffset = $520; {Where the EGA save save is stored}
- IntComOffset = $528; {Where the interapps comm area is stored}
- EMScntOffset = $538; {Where count of EMS active pages is stored}
- EMSmapOffset = $53A; {Where the page map is stored}
-
- WatchID = 'TSR WATCHER'; {Marking string for WATCH}
-
- {Offsets into resident copy of WATCH.COM for data storage}
- WatchOffset = $81;
- NextChange = $104;
- ChangeVectors = $220;
- OrigVectors = $620;
- CurrVectors = $A20;
- MaxChanges = 128; {Maximum number of vector changes stored in WATCH}
-
- type
- Registers =
- record
- case Integer of
- 1 : (ax, bx, cx, dx, bp, si, di, ds, es, flags : Integer);
- 2 : (al, ah, bl, bh, cl, ch, dl, dh : Byte);
- end;
-
- HandlePageRecord =
- record
- handle : Integer;
- numpages : Integer;
- end;
-
- PageArray = array[1..MaxHandles] of HandlePageRecord;
- PageArrayPtr = ^PageArray;
-
- Block =
- record {Store info about each memory block}
- mcb : Integer;
- psp : Integer;
- releaseIt : Boolean;
- end;
-
- BlockType = 0..MaxBlocks;
- BlockArray = array[BlockType] of Block;
-
- HexString = string[4];
- Pathname = string[64];
- AllStrings = string[255];
-
- var
- Blocks : BlockArray;
- watchBlock, bottomBlock, blockNum : BlockType;
-
- markName : AllStrings;
- Regs : Registers;
-
- FilMarkHandles, ReturnCode, StartMCB, StoredHandles, EMShandles : Integer;
- UseWatch, Debug, Revector8259, DealWithEMS,
- KeepMark, MemMark, FilMark, Junk : Boolean;
-
- FilMarkPageMap, Map, StoredMap : PageArrayPtr;
- TrappedBytes : Real;
-
- {Save areas read in from file mark}
- Vectors : array[0..1023] of Byte;
- EGAsavTable : array[0..7] of Byte;
- IntComTable : array[0..15] of Byte;
-
- procedure Abort(msg : AllStrings);
- {-Halt in case of error}
- begin
- WriteLn(msg);
- Halt(1);
- end {Abort} ;
-
- procedure Halt(ReturnCode : Integer);
- {-Replace Turbo halt with one that doesn't restore any interrupts}
- begin
- Close(Output);
- with Regs do begin
- ah := $4C;
- al := Lo(ReturnCode);
- MsDos(Regs);
- end;
- end {Halt} ;
-
- procedure FindTheBlocks;
- {-Scan memory for the allocated memory blocks}
- const
- MidBlockID = $4D; {Byte DOS uses to identify part of MCB chain}
- EndBlockID = $5A; {Byte DOS uses to identify last block of MCB chain}
- var
- mcbSeg : Integer; {Segment address of current MCB}
- nextSeg : Integer; {Computed segment address for the next MCB}
- gotFirst : Boolean; {True after first MCB is found}
- gotLast : Boolean; {True after last MCB is found}
- idbyte : Byte; {Byte that DOS uses to identify an MCB}
-
- function GetStartMCB : Integer;
- {-Return the first MCB segment}
- begin
- Regs.ah := $52;
- MsDos(Regs);
- GetStartMCB := MemW[Regs.es:(Regs.bx-2)];
- end {Getstartmcb} ;
-
- procedure StoreTheBlock(var mcbSeg, nextSeg : Integer;
- var gotFirst, gotLast : Boolean);
- {-Store information regarding the memory block}
- var
- nextID : Byte;
- pspAdd : Integer; {Segment address of the current PSP}
- mcbLen : Integer; {Size of the current memory block in paragraphs}
-
- begin
-
- mcbLen := MemW[mcbSeg:3]; {Size of the MCB in paragraphs}
- nextSeg := Succ(mcbSeg+mcbLen); {Where the next MCB should be}
- pspAdd := MemW[mcbSeg:1]; {Address of program segment prefix for MCB}
- nextID := Mem[nextSeg:0];
-
- if gotLast or (nextID = EndBlockID) or (nextID = MidBlockID) then begin
- blockNum := Succ(blockNum);
- gotFirst := True;
- with Blocks[blockNum] do begin
- mcb := mcbSeg;
- psp := pspAdd;
- end;
- end;
-
- end {Storetheblock} ;
-
- begin
-
- {Initialize}
- StartMCB := GetStartMCB;
- mcbSeg := StartMCB;
- gotFirst := False;
- gotLast := False;
- blockNum := 0;
-
- {Scan all memory until the last block is found}
- repeat
- idbyte := Mem[mcbSeg:0];
- if idbyte = MidBlockID then begin
- StoreTheBlock(mcbSeg, nextSeg, gotFirst, gotLast);
- if gotFirst then
- mcbSeg := nextSeg
- else
- mcbSeg := Succ(mcbSeg);
- end else if gotFirst and (idbyte = EndBlockID) then begin
- gotLast := True;
- StoreTheBlock(mcbSeg, nextSeg, gotFirst, gotLast);
- end else
- {Start block was invalid}
- Abort('Corrupted allocation chain or program error....');
- until gotLast;
-
- end {Findtheblocks} ;
-
- function StUpcase(s : AllStrings) : AllStrings;
- {-Return the uppercase string}
- var
- i : Byte;
-
- begin
- for i := 1 to Length(s) do
- s[i] := UpCase(s[i]);
- StUpcase := s;
- end {Stupcase} ;
-
- function FindMark(markName, MarkID : AllStrings;
- MarkOffset : Integer;
- var MemMark, FilMark : Boolean;
- var b : BlockType) : Boolean;
- {-Find the last memory block matching idstring at offset idoffset}
-
- function HasIDstring(segment : Integer;
- idString : AllStrings;
- idOffset : Integer) : Boolean;
- {-Return true if idstring is found at segment:idoffset}
- var
- tString : AllStrings;
- len : Byte;
- begin
- len := Length(idString);
- tString[0] := Chr(len);
- Move(Mem[segment:idOffset], tString[1], len);
- HasIDstring := (tString = idString);
- end {HasIDstring} ;
-
- function GetMarkName(segment : Integer) : AllStrings;
- {-Return a cleaned up mark name from the segment's PSP}
- var
- tString : AllStrings;
- tlen : Byte absolute tString;
- begin
- Move(Mem[segment:$80], tString[0], 128);
- while (tlen > 0) and ((tString[1] = ' ') or (tString[1] = ^I)) do
- Delete(tString, 1, 1);
- while (tlen > 0) and ((tString[tlen] = ' ') or (tString[tlen] = ^I)) do
- tlen := Pred(tlen);
- GetMarkName := StUpcase(tString);
- end; {GetMarkName}
-
- function MatchMemMark(segment : Integer;
- markName : AllStrings;
- var b : BlockType) : Boolean;
- {-Return true if MemMark is unnamed or matches current name}
- var
- tString : AllStrings;
- FoundIt : Boolean;
- begin
- {Check the mark name stored in the PSP of the mark block}
- tString := GetMarkName(segment);
- if (markName <> '') then begin
- FoundIt := (tString = StUpcase(markName));
- if not(FoundIt) then
- if (tString <> '') and (tString[1] = ProtectChar) then
- {Current mark is protected, stop searching}
- b := 1;
- end else if (tString <> '') and (tString[1] = ProtectChar) then begin
- {Stored mark name is protected}
- FoundIt := False;
- {Stop checking}
- b := 1;
- end else
- {Match any mark}
- FoundIt := True;
- if not(FoundIt) then
- b := Pred(b);
- MatchMemMark := FoundIt;
- end {MatchMemMark} ;
-
- function MatchFilMark(segment : Integer;
- markName : AllStrings;
- var b : BlockType) : Boolean;
- {-Return true if FilMark is unnamed or matches current name}
- var
- tString : AllStrings;
- FoundIt : Boolean;
-
- function ExistFile(path : AllStrings) : Boolean;
- {-Return true if file exists}
- var
- f : file;
- begin
- Assign(f, path);
- {$I-}
- Reset(f);
- {$I+}
- ExistFile := (IOResult = 0);
- Close(f);
- end; {Existfile}
-
- begin
- {Check the mark name stored in the PSP of the mark block}
- tString := GetMarkName(segment);
- if (markName <> '') then begin
- markName := StUpcase(markName);
- FoundIt := (tString = markName);
- if FoundIt then begin
- {Assure named file exists}
- WriteLn('Finding mark file ', markName);
- FoundIt := ExistFile(markName);
- if not(FoundIt) then
- {Stop checking}
- b := 1;
- end;
- end else
- {File marks must be named on RELEASE command line}
- FoundIt := False;
- if not(FoundIt) then
- b := Pred(b);
- MatchFilMark := FoundIt;
- end {MatchFilMark} ;
-
- begin
- {Scan from the last block down to find the last MARK TSR}
- b := blockNum;
- MemMark := False;
- FilMark := False;
- repeat
- if Blocks[b].psp = CSeg then
- {Assure this program's command line is not matched}
- b := Pred(b)
- else if HasIDstring(Blocks[b].psp, MarkID, MarkOffset) then
- {An in-memory mark}
- MemMark := MatchMemMark(Blocks[b].psp, markName, b)
- else if HasIDstring(Blocks[b].psp, FmarkID, FmarkOffset) then
- {A file mark}
- FilMark := MatchFilMark(Blocks[b].psp, markName, b)
- else
- {Not a mark}
- b := Pred(b);
- until (b < 1) or MemMark or FilMark;
- FindMark := MemMark or FilMark;
- end {Findmark} ;
-
- function Hex(i : Integer) : HexString;
- {-Return hex representation of integer}
- const
- hc : array[0..15] of Char = '0123456789ABCDEF';
- var
- l, h : Byte;
- begin
- l := Lo(i);
- h := Hi(i);
- Hex := hc[h shr 4]+hc[h and $F]+hc[l shr 4]+hc[l and $F];
- end {Hex} ;
-
- procedure ReadMarkFile(markName : AllStrings);
- {-Read the mark file info into memory}
- var
- f : file;
- begin
- Assign(f, markName);
- Reset(f, 1);
-
- {Read the vector table from the mark file, into a temporary memory area}
- BlockRead(f, Vectors, 1024);
-
- {Read the BIOS miscellaneous save areas into temporary tables}
- BlockRead(f, EGAsavTable, 8);
- BlockRead(f, IntComTable, 16);
-
- {Read the number of EMS handles stored}
- BlockRead(f, FilMarkHandles, 2);
-
- {Get a page map area and read the page map into it}
- GetMem(FilMarkPageMap, 4*FilMarkHandles);
- BlockRead(f, FilMarkPageMap^, 4*FilMarkHandles);
- Close(f);
-
- if not(KeepMark) then
- {Delete the mark file so it causes no mischief later}
- Erase(f);
- end {ReadMarkFile} ;
-
- procedure CopyVectors(bottomBlock : BlockType);
- {-Put interrupt vectors back into table}
- var
- bottompsp : Integer;
-
- procedure Reset8259;
- {-Reset the 8259 interrupt controller to its powerup state}
- {-Interrupts assumed OFF prior to calling this routine}
-
- function ATmachine : Boolean;
- {-Return true if machine is AT class}
- var
- machtype : Byte absolute $FFFF : $000E;
- begin
- ATmachine := (machtype = $FC);
- end {ATmachine} ;
-
- procedure Reset8259PC;
- {-Reset the 8259 on a PC class machine}
- begin
- inline(
- $E4/$21/ { in al,$21}
- $88/$C4/ { mov ah,al}
- $B0/$13/ { mov al,+$13}
- $E6/$20/ { out $20,al}
- $B0/$08/ { mov al,+$08}
- $E6/$21/ { out $21,al}
- $B0/$09/ { mov al,+$09}
- $E6/$21/ { out $21,al}
- $88/$E0/ { mov al,ah}
- $E6/$21 { out $21,al}
- );
- end {Reset8259PC} ;
-
- procedure Reset8259AT;
- {-Reset the 8259 interrupt controllers on an AT machine}
- begin
- inline(
- $32/$C0/ { xor al,al }
- $E6/$F1/ { out 0f1h,al ; Switch off an 80287 if necessary}
- {Set up master 8259 }
- $E4/$21/ { in al,21h ; Get current interrupt mask }
- $8A/$E0/ { mov ah,al ; save it }
- $B0/$11/ { mov al,11h }
- $E6/$20/ { out 20h,al }
- $EB/$00/ { jmp short $+2 }
- $B0/$08/ { mov al,8 ; Set up main interrupt vector number}
- $E6/$21/ { out 21h,al }
- $EB/$00/ { jmp short $+2 }
- $B0/$04/ { mov al,4 }
- $E6/$21/ { out 21h,al }
- $EB/$00/ { jmp short $+2 }
- $B0/$01/ { mov al,1 }
- $E6/$21/ { out 21h,al }
- $EB/$00/ { jmp short $+2 }
- $8A/$C4/ { mov al,ah }
- $E6/$21/ { out 21h,al }
- {Set up slave 8259 }
- $E4/$A1/ { in al,0a1h ; Get current interrupt mask }
- $8A/$E0/ { mov ah,al ; save it }
- $B0/$11/ { mov al,11h }
- $E6/$A0/ { out 0a0h,al }
- $EB/$00/ { jmp short $+2 }
- $B0/$70/ { mov al,70h }
- $E6/$A1/ { out 0a1h,al }
- $B0/$02/ { mov al,2 }
- $EB/$00/ { jmp short $+2 }
- $E6/$A1/ { out 0a1h,al }
- $EB/$00/ { jmp short $+2 }
- $B0/$01/ { mov al,1 }
- $E6/$A1/ { out 0a1h,al }
- $EB/$00/ { jmp short $+2 }
- $8A/$C4/ { mov al,ah ; Reset previous interrupt state }
- $E6/$A1 { out 0a1h,al }
- );
- end {Reset8259AT} ;
-
- begin
- if ATmachine then
- Reset8259AT
- else
- Reset8259PC;
- end {Reset8259} ;
-
- begin
-
- {Interrupts off}
- inline($FA);
-
- {Reset 8259 if requested}
- if Revector8259 then
- Reset8259;
-
- {Restore the main interrupt vector table and the misc save areas}
- if FilMark then begin
- Move(Vectors, Mem[0:0], 1024);
- Move(EGAsavTable, Mem[$40:$A8], 8);
- Move(IntComTable, Mem[$40:$F0], 16);
- end else begin
- bottompsp := Blocks[bottomBlock].psp;
- Move(Mem[bottompsp:VectorOffset], Mem[0:0], 1024);
- Move(Mem[bottompsp:EGAsavOffset], Mem[$40:$A8], 8);
- Move(Mem[bottompsp:IntComOffset], Mem[$40:$F0], 16);
- end;
-
- {Interrupts on}
- inline($FB);
-
- {Move the old termination/break/error addresses into this program}
- Move(Mem[0:$88], Mem[CSeg:$0A], 12);
-
- {Restore the "parent address" used by the DOS EXIT command to remove a shell}
- Move(Mem[CSeg:$0C], Mem[CSeg:$16], 2);
-
- end {CopyVectors} ;
-
- procedure MarkBlocks(bottomBlock : BlockType);
- {-Mark those blocks to be released}
- var
- b : BlockType;
- commandPsp, markPsp : Integer;
- ch : Char;
-
- procedure BatchWarning(b : BlockType);
- {-Warn about the trapping effect of batch files}
- var
- t : BlockType;
-
- function Cardinal(i : Integer) : Real;
- {-Return unsigned integer 0..65535 in a real}
- begin
- if i < 0 then
- Cardinal := 65536.0+i
- else
- Cardinal := i;
- end {Cardinal} ;
-
- begin
- WriteLn('Memory space for TSRs installed prior to batch file');
- WriteLn('will not be released until batch file completes.');
- WriteLn;
- ReturnCode := 1;
- {Accumulate number of bytes temporarily trapped}
- for t := 1 to b do
- if Blocks[t].releaseIt then
- TrappedBytes := TrappedBytes+16.0*Cardinal(MemW[Blocks[t].mcb:3]);
- end {BatchWarning} ;
-
- begin
-
- commandPsp := Blocks[2].psp;
- markPsp := Blocks[bottomBlock].psp;
-
- for b := 1 to blockNum do
- with Blocks[b] do
- if (b < bottomBlock) then begin
- {Release any trapped environment block}
- if KeepMark then
- releaseIt := (psp <> CSeg) and (psp xor $8000 > markPsp xor $8000)
- else
- releaseIt := (psp <> CSeg) and (psp xor $8000 >= markPsp xor $8000);
- end else if (psp = commandPsp) then begin
- {Don't release blocks owned by COMMAND.COM}
- releaseIt := False;
- BatchWarning(b);
- end else if KeepMark then
- {Release all but RELEASE and the mark}
- releaseIt := (psp <> CSeg) and (psp <> markPsp)
- else
- {Release all but RELEASE itself}
- releaseIt := (psp <> CSeg);
-
- if Debug then begin
- for b := 1 to blockNum do with Blocks[b] do
- WriteLn(b:3, ' ', Hex(psp), ' ', Hex(mcb), ' ', releaseIt);
- Read(Kbd, ch);
- end;
-
- end {MarkBlocks} ;
-
- procedure ReleaseMem;
- {-Release DOS memory marked for release}
- var
- b : BlockType;
- begin
- with Regs do
- for b := 1 to blockNum do
- with Blocks[b] do
- if releaseIt then begin
- ah := $49;
- {The block is always 1 paragraph above the MCB}
- es := Succ(mcb);
- MsDos(Regs);
- if Odd(flags) then begin
- WriteLn('Could not release block at segment ', Hex(es));
- Abort('Memory may be a mess... Please reboot');
- end;
- end;
- end {Releasemem} ;
-
- procedure UpdateWatch(watchBlock : BlockType);
- {-Write a new watch data area based on the release and the original watch}
- type
- ChangeBlock =
- record
- VecID : Integer;
- VecOfs : Integer;
- VecSeg : Integer;
- PatchWord : Integer;
- end;
- var
- changes : array[0..MaxChanges] of ChangeBlock;
- p : ^ChangeBlock;
- watchseg, c, o, i, actualmax : Integer;
- KeepPSP : Boolean;
-
- function WillKeepPSP(pspAdd : Integer) : Boolean;
- {-Return true if this psp address will be kept}
- var
- b : BlockType;
- begin
- for b := 1 to blockNum do
- with Blocks[b] do
- if psp = pspAdd then begin
- WillKeepPSP := not(releaseIt);
- Exit;
- end;
- end {WillKeepPSP} ;
-
- begin
-
- {Initialize}
- watchseg := Blocks[watchBlock].psp;
- actualmax := MemW[watchseg:NextChange];
-
- {Transfer changes from WATCH into a buffer array}
- i := 0;
- o := 0;
- while i < actualmax do begin
- p := Ptr(watchseg, ChangeVectors+i);
- Move(p^, changes[o], SizeOf(ChangeBlock));
- i := i+SizeOf(ChangeBlock);
- o := Succ(o);
- end;
-
- {Determine which change records to keep and transfer them back to WATCH}
- KeepPSP := True;
- i := 0;
- for c := 0 to Pred(o) do begin
- with changes[c] do
- if VecID = $FFFF then
- {This record starts a new PSP. See if PSP is kept in memory}
- KeepPSP := WillKeepPSP(VecOfs);
- if KeepPSP then begin
- p := Ptr(watchseg, ChangeVectors+i);
- Move(changes[c], p^, SizeOf(ChangeBlock));
- i := i+SizeOf(ChangeBlock);
- end;
- end;
- MemW[watchseg:NextChange] := i;
-
- {Update the WATCH image of the vector table to whatever's current}
- Move(Mem[0:0], Mem[watchseg:CurrVectors], 1024);
-
- end {UpdateWatch} ;
-
- function EMSpresent : Boolean;
- {-Return true if EMS memory manager is present}
- var
- f : file;
- begin
- {"file handle" defined by the expanded memory manager at installation}
- Assign(f, 'EMMXXXX0');
- {$I-}
- Reset(f);
- {$I+}
- EMSpresent := (IOResult = 0);
- Close(f);
- end {EMSpresent} ;
-
- procedure RestoreEMSmap;
- {-Restore EMS to state at time of mark}
-
- function GetHandles(bottomBlock : BlockType; EMScntOffset : Integer) : Integer;
- {-Return the number of handles stored by mark}
- begin
- if FilMark then
- GetHandles := FilMarkHandles
- else
- GetHandles := MemW[Blocks[bottomBlock].psp:EMScntOffset];
- end {Gethandles} ;
-
- function GetStoredMap(bottomBlock : BlockType; EMSmapOffset : Integer) : PageArrayPtr;
- {-Returns a pointer to the stored page array}
- begin
- if FilMark then
- GetStoredMap := FilMarkPageMap
- else
- GetStoredMap := Ptr(Blocks[bottomBlock].psp, EMSmapOffset);
- end {GetStoredMap} ;
-
- procedure EMSpageMap(var PageMap : PageArray; var EMShandles:integer);
- {-return an array of the allocated memory blocks}
- begin
- regs.ah := $4D;
- regs.es := Seg(PageMap);
- regs.di := Ofs(PageMap);
- regs.bx := 0;
- Intr(EMSinterrupt, regs);
- if regs.ah <> 0 then begin
- WriteLn('EMS device not responding');
- emshandles:=0;
- end else
- emshandles:=regs.bx;
- end {EMSpageMap} ;
-
- procedure ReleaseEMSblocks(var oldmap, newmap : PageArray);
- {-Release those EMS blocks allocated since MARK was installed}
- var
- o, n, nhandle : Integer;
-
- procedure EMSdeallocate(EMShandle : Integer);
- {-Release the allocated expanded memory}
- begin
- Regs.ah := $45;
- Regs.dx := EMShandle;
- Intr(EMSinterrupt, Regs);
- if Regs.ah <> 0 then begin
- WriteLn('Program error or EMS device not responding');
- Abort('EMS memory may be a mess... Please reboot');
- end;
- end {EMSdeallocate} ;
-
- begin
- for n := 1 to EMShandles do begin
- {Scan all current handles}
- nhandle := newmap[n].handle;
- if StoredHandles > 0 then begin
- {See if current handle matches one stored by MARK}
- o := 1;
- while (oldmap[o].handle <> nhandle) and (o <= StoredHandles) do
- o := Succ(o);
- {If not, deallocate the current handle}
- if (o > StoredHandles) then
- EMSdeallocate(nhandle);
- end else
- {No handles stored by MARK, deallocate all current handles}
- EMSdeallocate(nhandle);
- end;
- end {ReleaseEMSblocks} ;
-
- begin
- {Get the existing EMS page map}
- GetMem(Map, 2048);
- EMSpageMap(Map^, EMShandles);
- if EMShandles > MaxHandles then
- WriteLn('EMS process count exceeds capacity of RELEASE - no action taken')
- else if EMShandles <> 0 then begin
- {See how many handles were active when MARK was installed}
- StoredHandles := GetHandles(bottomBlock, EMScntOffset);
- {Get the stored page map}
- StoredMap := GetStoredMap(bottomBlock, EMSmapOffset);
- {Compare the two maps and deallocate pages not in the stored map}
- ReleaseEMSblocks(StoredMap^, Map^);
- end;
- end {RestoreEMSmap} ;
-
- procedure GetOptions;
- {-Analyze command line for options}
- var
- arg : AllStrings;
- arglen : Byte absolute arg;
- i : Integer;
-
- procedure WriteHelp;
- {-Show the options}
- begin
- WriteLn('RELEASE ', Version, ', by TurboPower Software');
- WriteLn('====================================================');
- WriteLn('RELEASE removes memory-resident programs from memory');
- WriteLn('and restores the interrupt vectors to their state as');
- WriteLn('found prior to the installation of a MARK.');
- WriteLn('RELEASE manages both normal DOS memory and also');
- WriteLn('Lotus/Intel Expanded Memory. If WATCH has been installed,');
- WriteLn('RELEASE will update the WATCH data area for the TSRs');
- WriteLn('released.');
- WriteLn;
- WriteLn('RELEASE accepts the following command line syntax:');
- WriteLn;
- WriteLn(' RELEASE [MarkName] [Options]');
- WriteLn;
- WriteLn('Options may be preceded by either / or -. Valid options');
- WriteLn('are as follows:');
- WriteLn;
- WriteLn(' /K release memory, but Keep the mark in place.');
- writeln(' /N do Not touch EMS memory in any way.');
- WriteLn(' /R Revector the 8259 interrupt controller to its');
- WriteLn(' powerup state.');
- WriteLn(' /? write this help screen.');
- Halt(1);
- end {WriteHelp} ;
-
- begin
-
- WriteLn;
-
- {Initialize defaults}
- markName := '';
- Revector8259 := False;
- KeepMark := False;
- DealWithEMS := True;
- ReturnCode := 0;
- TrappedBytes := 0.0;
- Debug := False;
-
- i := 1;
- while i <= ParamCount do begin
- arg := ParamStr(i);
- if (arg[1] = '?') then
- WriteHelp
- else if (arg[1] = '-') or (arg[1] = '/') then
- case arglen of
- 1 : Abort('Missing command option following '+arg);
- 2 : case UpCase(arg[2]) of
- '?' : WriteHelp;
- 'R' : Revector8259 := True;
- 'K' : KeepMark := True;
- 'N' : DealWithEMS := False;
- 'D' : Debug := True;
- else
- Abort('Unknown command option: '+arg);
- end;
- else
- Abort('Unknown command option: '+arg);
- end
- else
- {Named mark}
- markName := arg;
- i := Succ(i);
- end;
-
- end {GetOptions} ;
-
- begin
-
- {Analyze command line for options}
- GetOptions;
-
- {Get all allocated memory blocks in normal memory}
- FindTheBlocks;
-
- {Find the last one marked with the MARK idstring, and MarkName if specified}
- if not(FindMark(markName, MarkID, MarkOffset, MemMark, FilMark, bottomBlock)) then
- Abort('No matching marker found, or protected marker encountered.');
-
- {Find the watch block, if any}
- UseWatch := FindMark('', WatchID, WatchOffset, Junk, Junk, watchBlock);
-
- {Mark those blocks to be released}
- MarkBlocks(bottomBlock);
-
- {Get file mark information into memory}
- if FilMark then
- ReadMarkFile(markName);
-
- {Copy the vector table from the MARK copy}
- CopyVectors(bottomBlock);
-
- {Update the watch block if requested}
- if UseWatch then
- {The WATCH ID was found in memory}
- if not(Blocks[watchBlock].releaseIt) then
- {Watch itself won't be released}
- UpdateWatch(watchBlock);
-
- {Release normal memory marked for release}
- ReleaseMem;
-
- {Deal with expanded memory}
- if DealWithEMS then
- if EMSpresent then
- RestoreEMSmap;
-
- {Write success message}
- Write('RELEASE ', Version, ' - Memory released above last MARK ');
- if markName <> '' then
- Write('(', StUpcase(markName), ')');
- WriteLn;
-
- if ReturnCode <> 0 then
- WriteLn(TrappedBytes:0:0, ' bytes temporarily trapped until batch file completes');
-
- Halt(ReturnCode);
- end.